﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Swift
{
    /// <summary>
    /// Flow Control
    /// </summary>
    public static class FC
    {
        #region For

        public static void For(int end, Action<int> f, Func<bool> continueCondition = null)
        {
            For(0, end, f, continueCondition);
        }

        public static void For(int start, int end, Action<int> f, Func<bool> continueCondition = null)
        {
            for (int i = start; i < end ; i++)
                f(i);
        }

        public static void ForFromTo(int start, int end, Action<int> f, Func<bool> continueCondition = null)
        {
            if (start <= end)
                for (int i = start; i < end && (continueCondition == null || continueCondition()); i++)
                    f(i);
            else
                for (int i = start; i > end && (continueCondition == null || continueCondition()); i--)
                    f(i);
        }

        public static void ForFromTo2(int start1, int end1, int start2, int end2, Action<int, int> f, Func<bool> continueCondition = null)
        {
            ForFromTo(start1, end1, (i) =>
            {
                ForFromTo(start2, end2, (j) =>
                {
                    f(i, j);
                }, continueCondition);
            }, continueCondition);
        }

        public static void For2(int start1, int end1, int start2, int end2, Action<int, int> f, Func<bool> continueCondition = null)
        {
            For(start1, end1, (i) =>
            {
                For(start2, end2, (j) =>
                {
                    f(i, j);
                }, continueCondition);
            }, continueCondition);
        }

        public static void For2(int end1, int end2, Action<int, int> f, Func<bool> continueCondition = null)
        {
            For2(0, end1, 0, end2, f, continueCondition);
        }

        public static void ForEach<T>(IEnumerable<T> set, Action<int, T> f, Func<bool> continueCondition = null)
        {
            if (set == null)
                return;

            var i = 0;
            foreach (var d in set)
            {
                if (continueCondition != null && !continueCondition())
                    break;

                f(i++, d);
            }
        }

        // Map函数
        public static RT[] Map<T, RT>(this T[] preData, Func<T, RT> f)
        {
            RT[] postData = new RT[preData.Length];
            if (preData == null)
                return null;

            var i = 0;
            foreach (var d in preData)
                postData[i++] = f(d);

            return postData;
        }

        public static List<RT> Map<T, RT>(this List<T> preData, Func<T, RT> f)
        {
            var postData = new List<RT>();
            if (preData == null)
                return null;
            
            foreach (var d in preData)
                postData.Add(f(d));

            return postData;
        }

        public static void RectForCenterAt(int cx, int cy, int halfW, int halfH, Action<int, int> f, Func<bool> continueCondition = null)
        {
            var left = cx - halfW;
            var right = cx + halfW;
            var top = cy - halfH;
            var bottom = cy + halfH;
            RectFor(left, right, top, bottom, f, continueCondition);
        }

        // top > bottom
        public static void RectFor(int left, int right, int bottom, int top, Action<int,int> f, Func<bool> continueCondition = null)
        {
            // 左右(包括上下左右4个角落)
            For(bottom, top + 1, (_y) =>
            {
                f(left, _y);
                if (continueCondition != null && continueCondition()) return;
                f(right, _y);
                if (continueCondition != null && continueCondition()) return;
            }, continueCondition);

            // 上下(不包括4个角落)
            For(left + 1, right, (_x) =>
            {
                f(_x, bottom);
                if (continueCondition != null && continueCondition()) return;
                f(_x, top);
                if (continueCondition != null && continueCondition()) return;
            }, continueCondition);
        }

        public enum SquareForSeq
        {
            Default,
            PerpendicularFirst, // 上下左右4个位置优先
        }

        // 方形遍历，只遍历方形的4条边
        // cx,cy=中心点坐标
        // r=半径，至少1
        // 以r==2为例，seq == Default, 遍历顺序如下
        // 9CEGA
        // 7   8
        // 5 * 6
        // 3   4
        // 1BDF2
        public static void SquareFor(int cx, int cy, int r, Action<int, int> f, 
            SquareForSeq seq = SquareForSeq.Default, Func<bool> continueCondition = null)
        {
            if (r < 0 || (continueCondition != null && !continueCondition()))
                return;

            if (r == 0)
            {
                f(cx, cy);
                return;
            }

            if (seq == SquareForSeq.PerpendicularFirst)
            {
                f(cx - r, cy);  // 左
                if (continueCondition != null && continueCondition()) return;
                f(cx + r, cy);  // 右
                if (continueCondition != null && continueCondition()) return;
                f(cx, cy + r);  // 上
                if (continueCondition != null && continueCondition()) return;
                f(cx, cy - r);  // 下
                if (continueCondition != null && continueCondition()) return;

                // 左右(包括上下左右4个角落)
                For(cy - r, cy + r + 1, (_y) =>
                {
                    if (_y == cy)
                        return;

                    f(cx - r, _y);
                    if (continueCondition != null && continueCondition()) return;
                    f(cx + r, _y);
                    if (continueCondition != null && continueCondition()) return;
                }, continueCondition);
                // 上下(不包括4个角落)
                For(cx - r + 1, cx + r, (_x) =>
                {
                    if (_x == cx)
                        return;

                    f(_x, cy - r);
                    if (continueCondition != null && continueCondition()) return;
                    f(_x, cy + r);
                    if (continueCondition != null && continueCondition()) return;
                }, continueCondition);
            }
            else
            {
                // 左右(包括上下左右4个角落)
                For(cy - r, cy + r + 1, (_y) =>
                {
                    f(cx - r, _y);
                    if (continueCondition != null && continueCondition()) return;
                    f(cx + r, _y);
                    if (continueCondition != null && continueCondition()) return;
                }, continueCondition);

                // 上下(不包括4个角落)
                For(cx - r + 1, cx + r, (_x) =>
                {
                    f(_x, cy - r);
                    if (continueCondition != null && continueCondition()) return;
                    f(_x, cy + r);
                    if (continueCondition != null && continueCondition()) return;
                }, continueCondition);
            }
        }
        public static void SquareFor2(int cx, int cy, int start_r, int end_r, Action<int, int> f, SquareForSeq seq = SquareForSeq.Default, Func<bool> continueCondition = null)
        {
            for (int r = start_r; r < end_r && (continueCondition == null || continueCondition()); r++)
                SquareFor(cx, cy, r, f, seq, continueCondition);
        }
        
        // 斜的一圈一圈遍历，也即距离相等的一圈遍历
        public static void ObliqueSquareFor(int cx, int cy, int r, Action<int, int> f, Func<bool> continueCondition = null)
        {
            if (r < 0 || (continueCondition != null && !continueCondition()))
                return;

            if (r == 0)
            {
                f(cx, cy);
                return;
            }

            int left = cx - r;
            int right = cx + r;
            int top = cy + r;
            int bottom = cy - r;

            f(left, cy);  // 左
            if (continueCondition != null && !continueCondition()) return;
            f(right, cy);  // 右
            if (continueCondition != null && !continueCondition()) return;
            f(cx, top);  // 上
            if (continueCondition != null && !continueCondition()) return;
            f(cx, bottom);  // 下
            if (continueCondition != null && !continueCondition()) return;

            for (int i = 1; i < r && (continueCondition == null || continueCondition()); i++)
            {
                f(left + i, cy + i);     // 左-->上
                if (continueCondition != null && !continueCondition()) return;
                f(left + i, cy - i);    // 左-->下
                if (continueCondition != null && !continueCondition()) return;

                f(right - i, cy + i);    // 右-->上
                if (continueCondition != null && !continueCondition()) return;
                f(right - i, cy - i);   // 右-->下
                if (continueCondition != null && !continueCondition()) return;
            }
        }

        public static void ObliqueSquareFor2(int cx, int cy, int start_r, int end_r, Action<int, int> f, Func<bool> continueCondition = null)
        {
            for (int r = start_r; r < end_r && (continueCondition == null || continueCondition()); r++)
                ObliqueSquareFor(cx, cy, r, f, continueCondition);
        }

        public static bool Contains<T>(this IEnumerable<T> itor, Func<T, bool> f)
        {
            foreach (var it in itor)
            {
                if (f(it))
                    return true;
            }

            return false;
        }

        public static bool Contains<T>(this IEnumerable<T> itor, IEnumerable<T> ts)
        {
            if (ts == null) return false;

            foreach (var t in ts)
            {
                if (!itor.Contains(t))
                    return false;
            }

            return true;
        }

        public static bool Contains<T>(this T[] arr, T e)
        {
            return arr.Contains((elem) => e.Equals(elem));
        }

        // 满足条件的对象映射为新列表
        public static List<T> Select<T>(this IEnumerable<T> origin, Func<T, bool> filter)
        {
            var lst = new List<T>();
            foreach (var e in origin)
                if (filter(e))
                    lst.Add(e);

            return lst;
        }

        public static void Travel<T>(this IEnumerable<T> src, Action<T> fun)
        {
            if (src == null || fun == null)
                return;

            foreach (var e in src)
                fun(e);
        }

        #endregion

        #region Action SafeCall

        public static void SC(this Action a)
        {
            if (a != null)
                a();
        }

        public static void SC<T>(this Action<T> a, T p)
        {
            if (a != null)
                a(p);
        }

        public static void SC<T1, T2>(this Action<T1, T2> a, T1 p1, T2 p2)
        {
            if (a != null)
                a(p1, p2);
        }

        public static void SC<T1, T2, T3>(this Action<T1, T2, T3> a, T1 p1, T2 p2, T3 p3)
        {
            if (a != null)
                a(p1, p2, p3);
        }

        public static void SC<T1, T2, T3, T4>(this Action<T1, T2, T3, T4> a, T1 p1, T2 p2, T3 p3, T4 p4)
        {
            if (a != null)
                a(p1, p2, p3, p4);
        }

        public static void SC<T1, T2, T3, T4, T5>(this Action<T1, T2, T3, T4, T5> a, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)
        {
            if (a != null)
                a(p1, p2, p3, p4, p5);
        }

        #endregion

        public static TDst[] ToArray<TSrc, TDst>(this IEnumerable<TSrc> src, Func<int, TSrc, Action, TDst> mapFun)
        {
            if (src == null)
                return null;

            var lst = new List<TDst>();
            var i = 0;
            foreach (var e in src)
            {
                var skip = false;
                var elem = mapFun(i++, e, () => { skip = true; });
                if (!skip)
                    lst.Add(elem);
            }

            return lst.ToArray();
        }

        public static int IndexOf<T>(this IEnumerable<T> set, T v, int startindex = 0)
        {
            return IndexOf(set, (item) => EqualityComparer<T>.Default.Equals(v, item), startindex);
        }

        public static int IndexOf<T>(this IEnumerable<T> set, Func<T, bool> filter, int startindex = 0)
        {
            if (set == null || set.Count() == 0)
                return -1;

            var i = 0;
            foreach (var item in set)
            {
                if (filter(item))
                    return i;

                i++;
            }

            return -1;
        }

        public static T[] SubArray<T>(this T[] arr, int start, int len)
        {
            T[] subArr = new T[len];
            FC.For(len, (i) => { subArr[i] = arr[start + i]; });
            return subArr;
        }

        public static void Assert(bool condition, string message)
        {
            if (!condition)
                throw new Exception(message);
        }

        public static void Assert(bool condition, Func<string> message)
        {
            if (!condition)
                throw new Exception(message());
        }

        // 列表原地移除重复元素
        public static void RemoveDuplicatedElems<T>(this List<T> lst)
        {
            var s = new HashSet<T>();
            foreach (var e in lst)
            {
                if (!s.Contains(e))
                    s.Add(e);
            }

            lst.Clear();
            lst.AddRange(s);
        }

        // 列表原地移除重复元素
        public static T[] RemoveDuplicatedElems<T>(this T[] arr)
        {
            var s = new HashSet<T>();
            foreach (var e in arr)
            {
                if (!s.Contains(e))
                    s.Add(e);
            }

            return s.ToArray();
        }

        // 去空后的新列表
        public static List<T> RemoveNullElems<T>(this List<T> lst)
        {
            var _1st = new List<T>();
            foreach (var e in lst)            
                if (e != null)
                    _1st.Add(e);
            
            return _1st;
        }

        // 去空后的新数组
        public static T[] RemoveNullElems<T>(this T[] lst)
        {
            var _1st = new List<T>();
            foreach (var e in lst)
                if (e != null)
                    _1st.Add(e);

            return _1st.ToArray();
        }

        // 尝试添加元素,如果已经有了就不重复添加
        public static List<T> AddWithoutDuplicated<T>(this List<T> set, T e)
        {
            if (!set.Contains(e))
                set.Add(e);

            return set;
        }

        // 将元素添加到特定的index,其后的元素向后移动
        public static void AddToIndex<T>(this List<T> list, T e, int index)
        {
            var listBack = new List<T>();
            while (list.Count > index)
            {
                listBack.Add(list[index]);
                list.RemoveAt(index);
            }
            list.Add(e);
            if (listBack.Count > 0)
                list.AddRange(listBack);            
        }

        // 求Dictionary<string, int>各项int的和
        public static int Total<T>(this Dictionary<T, int> d)
        {
            var sum = 0;
            foreach (var p in d.Values)
                sum += p;

            return sum;
        }

        // 从数组中或列表中随机获取一个元素
        public static T GetRandomOne<T>(this IEnumerable<T> set, SRandom srand) {
            if (set.Count() == 0)
                return default;

            return set.ToArray()[srand.Next(0, set.Count())];
        }

        public static T GetRandomOneWithFilter<T>(this IEnumerable<T>set, Predicate<T>filter, SRandom srand)
        {
            if (set.Count() == 0)
                return default;

            var lst = new List<T>();
            foreach (var e in set)
            {
                if(filter == null || filter(e))
                {
                    lst.Add(e);
                }
            }

            return lst.GetRandomOne(srand);
        }

        public static List<T> GetRandom<T>(this IEnumerable<T> set, int num, SRandom srand)
        {
            var list = new List<T>();

            if (set.Count() == 0)
                return list;

            // 如果元素总数不足则返回所有
            if(num > set.Count())
            {
                return set.ToList();
            }

            for (var i = 0; i < num; i++)
            {
                var e = GetRamdomElemWithFilter(set, (T t) => !list.Contains(t), srand);
                if (e != null && set.Contains(e))
                    list.Add(e);
            }
            return list;
        }

        public static void GetRandomOneAndAct<T>(this IEnumerable<T> set, SRandom srand, Action<T> act)
        {
            set.GetRandomAndAct(1, srand, act);
        }

        public static void GetRandomAndAct<T>(this IEnumerable<T> set, int num, SRandom srand, Action<T> act)
        {
            var list = set.GetRandom(num, srand);
            foreach (var elem in list)
            {
                act?.Invoke(elem);
            }
        }

        public static T GetRandomOneWithWeight<T>(this Dictionary<T, int> elemWithWeights, SRandom srand) {
            var es = elemWithWeights.GetRandomWithWeight(1, srand);
            if (es.Length == 0)
                return default(T);
            else return es[0];
        }

        public static T[] GetRandomWithWeight<T>(this Dictionary<T, int> elemWithWeights, int num, SRandom srand)
        {
            var list = new List<T>();

            if (elemWithWeights.Count() == 0)
                return list.ToArray();
            else if (elemWithWeights.Count() <= num)
                return FC.Map(elemWithWeights.ToArray(), (kv) => kv.Key);

            var totalWeight = 0;
            var ewws = new Dictionary<T, int>();
            foreach (var eww in elemWithWeights)
            {
                totalWeight += eww.Value;
                ewws[eww.Key] = eww.Value;
            }

            for (var i = 0; i < num; i++)
            {
                var index = srand.Next(0, totalWeight);
                var temp = 0;
                foreach(var eww in ewws)
                {
                    temp += eww.Value;
                    if(temp > index)
                    {
                        list.Add(eww.Key);
                        ewws.Remove(eww.Key);
                        break;
                    }
                }
            }

            return list.ToArray();
        }

        /// <summary>
        /// 从数组或列表中获取符合条件的随机的元素,有获取多个的重载
        /// </summary>
        public static T GetRamdomElemWithFilter<T>(this IEnumerable<T> set, Predicate<T> filter, SRandom srand) 
        {
            if (set.Count() == 0)
                return default(T);

            var list = new List<T>();
            foreach (var e in set)
                if (filter == null || filter(e))
                    list.Add(e);

            return list.GetRandomOne(srand);
        }

        /// <summary>
        /// 从数组或列表中获取多个符合条件的随机的元素,可能数量不够
        /// </summary>
        public static T[] GetRamdomElemWithFilter<T>(this IEnumerable<T> set, int num, Predicate<T> filter, SRandom srand)
        {
            var list = new List<T>();

            if (set.Count() == 0)
                return list.ToArray();

            bool newFilter(T t) => (filter == null || filter(t)) && !list.Contains(t);
            for (var i = 0; i < num; i++)
            {
                var e = GetRamdomElemWithFilter(set, newFilter, srand);
                if (e != null)
                    list.Add(e);
            }
            return list.ToArray();
        }

        public static Dictionary<T, int> GetRandomByWeight<T>(this IEnumerable<Dictionary<T, int>> dicWithWeight, T weightKey, SRandom srand)
        {
            var totalWeight = 0;
            foreach (var dic in dicWithWeight)
                totalWeight += dic[weightKey];

            var w = srand.Next(0, totalWeight);
            var tempW = 0;

            Dictionary<T, int> d = null;
            foreach(var dic in dicWithWeight)
            {
                tempW += dic[weightKey];
                if (w < tempW)
                {
                    d = dic;
                    break;
                }
            }
            return d;
        }

        // key为可转化为数字的string
        public static T[] DictionaryToArr<T>(this Dictionary<string, T> dic)
        {
            var list = new List<T>();
            foreach (var kv in dic)
                list[int.Parse(kv.Key)] = kv.Value;

            return list.ToArray();
        }

        // 在数组中找到与传入值index最靠近的满足条件的值,向前或向后搜索.找不到会返回最前或最后的值
        public static T FindNearestIndexValue<T>(this T[] arr, int n, Predicate<T> filter, bool forward = true)
        {
            var _n = n;
            while (!filter(arr[_n]) && _n > -1 && _n < arr.Length)
                _n = _n + (forward ? -1 : 1);

            return arr[_n];
        }

        // 字典转为数组,元素是key
        public static T1[] DicKeyToArr<T1, T2>(this Dictionary<T1, T2> dic) {
            var list = new List<T1>();
            foreach (var kv in dic)
                list.Add(kv.Key);

            return list.ToArray();
        }

        // 字典转为数组,元素是Value
        public static T2[] DicValueToArr<T1, T2>(this Dictionary<T1, T2> dic)
        {
            var list = new List<T2>();
            foreach (var kv in dic)
                list.Add(kv.Value);

            return list.ToArray();
        }

        public static KeyValuePair<K,V>[] DicToKVArray<K, V>(this Dictionary<K,V> dic)
        {
            var list = new List<KeyValuePair<K, V>>();
            foreach (var kv in dic)
                list.Add(kv);

            return list.ToArray();
        }
        
        public static List<string> SplitToStringList(this string s, params char[] c)
        {
            if (string.IsNullOrEmpty(s))
                return new List<string>();
            else
                return s.Split(c).ToList();
        }

        public static List<int> SplitToIntList(this string s, params char[] c)
        {
            if (string.IsNullOrEmpty(s))
            {
                return new List<int>();
            }
            else
            {
                var l = s.Split(c);
                var ls = new List<int>();
                foreach (var i in l)
                    ls.Add(int.Parse(i));
                return ls;
            }
        }

        public static void Add<T1,T2>(this Dictionary<T1, List<T2>> dic, T1 k, T2 v)
        {
            if (dic.ContainsKey(k))
                dic[k].Add(v);
            else
                dic[k] = new List<T2>() { v };
        }

        public static bool ContainsValue<T1, T2>(this Dictionary<T1, List<T2>> dic, T2 e)
        {
            foreach (var l in dic.Values)
            {
                if (l.Contains(e)) return true;
            }

            return false;
        }

        public static bool ContainsValue<T1,T2>(this Dictionary<T1,List<T2>> dic, Predicate<T2> filter)
        {
            foreach (var l in dic.Values)
            {
                if (l.Contains((e) => filter(e))) return true;
            }

            return false;
        }
    }
}
